<?php

declare(strict_types=1);

namespace NanQi\Hope\Process;

use Carbon\Carbon;
use Hyperf\Contract\ConfigInterface;
use Hyperf\Contract\StdoutLoggerInterface;
use Hyperf\Crontab\Event\CrontabDispatcherStarted;
use Hyperf\Crontab\Scheduler;
use Hyperf\Crontab\Strategy\Executor;
use Hyperf\Process\AbstractProcess;
use Hyperf\Utils\Coroutine;
use NanQi\Hope\Crontab\CrontabExecutor;
use Psr\Container\ContainerInterface;

class CoroutineCrontabDispatcherProcess extends AbstractProcess
{
    /**
     * @var string
     */
    public $name = 'crontab-dispatcher';

    /**
     * @var ConfigInterface
     */
    private $config;

    /**
     * @var Scheduler
     */
    private $scheduler;

    /**
     * @var StdoutLoggerInterface
     */
    private $logger;

    public function __construct(ContainerInterface $container)
    {
        parent::__construct($container);
        $this->config = $container->get(ConfigInterface::class);
        $this->scheduler = $container->get(Scheduler::class);
        $this->logger = $container->get(StdoutLoggerInterface::class);
    }

    public function handle(): void
    {
        $this->event->dispatch(new CrontabDispatcherStarted());
        while (1) {
            $this->sleep();
            $queue = $this->scheduler->schedule();
            while (! $queue->isEmpty()) {
                $crontab = $queue->dequeue();
                Coroutine::create(function () use ($crontab) {
                    if ($crontab->getExecuteTime() instanceof Carbon) {
                        $wait = $crontab->getExecuteTime()->getTimeStamp() - time();
                        $wait > 0 && \Swoole\Coroutine::sleep($wait);
                        $executor = $this->container->get(CrontabExecutor::class);
                        $executor->execute($crontab);
                    }
                });
            }
        }
    }

    public function isEnable($server): bool
    {
        return $this->config->get('crontab.enable', false);
    }

    private function sleep()
    {
        $current = date('s', time());
        $sleep = 60 - $current;
        $this->logger->debug('Crontab dispatcher sleep ' . $sleep . 's.');
        $sleep > 0 && \Swoole\Coroutine::sleep($sleep);
    }
}
